package com.yudaxian.bi.controller;


import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.yudaxian.bi.bizmq.BiMessageProducer;
import com.yudaxian.bi.common.BaseResponse;
import com.yudaxian.bi.common.Const;
import com.yudaxian.bi.common.ErrorCode;
import com.yudaxian.bi.common.ResultUtils;
import com.yudaxian.bi.constant.FileConstant;
import com.yudaxian.bi.exception.BusinessException;
import com.yudaxian.bi.manage.RedisLimitManage;
import com.yudaxian.bi.model.dto.chart.ChartAddRequest;
import com.yudaxian.bi.model.dto.chart.GenerateChartByAIRequest;
import com.yudaxian.bi.model.entity.Chart;
import com.yudaxian.bi.model.enums.ChartStatusEnum;
import com.yudaxian.bi.model.enums.FileUploadBizEnum;
import com.yudaxian.bi.model.query.ChartQuery;
import com.yudaxian.bi.model.vo.BiResponseVO;
import com.yudaxian.bi.service.ChartService;
import com.yudaxian.bi.utils.ExcelUtils;
import com.yudaxian.bi.utils.SparkAiUtils;
import com.yudaxian.bi.utils.StringTools;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 图表信息表 Controller
 */
@Slf4j
@Api(tags = "图表模块")
@RestController("chartController")
@RequestMapping("/chart")
public class ChartController {

	@Resource
	private ChartService chartService;

	@Resource
	private RedisLimitManage redisLimitManage;

	@Resource
	private SparkAiUtils sparkAiUtils;

	@Resource
	private ThreadPoolExecutor threadPoolExecutor;

	@Resource
	private BiMessageProducer biMessageProducer;

	/**
	 * 根据条件分页查询
	 */
	@ApiOperation(value = "分页查询图表信息", notes = "分页查询图表信息")
	@PostMapping("/loadDataList")
	public BaseResponse loadDataList(@RequestBody ChartQuery query){
		return ResultUtils.success(chartService.findListByPage(query));
	}

	/**
	 * 新增
	 */
	@PostMapping("/add")
	public BaseResponse add(@RequestBody ChartAddRequest bean) {
		chartService.add(bean);
		return ResultUtils.success(null);
	}


	/**
	 * 根据Id查询对象
	 */
	@PostMapping("/getChartById/{id}")
	public BaseResponse getChartById(@PathVariable Long id) {
		return ResultUtils.success(chartService.getChartById(id));
	}

	/**
	 * 根据Id修改对象
	 */
	@PostMapping("/updateChartById")
	public BaseResponse updateChartById(Chart bean,Long id) {
		chartService.updateChartById(bean,id);
		return ResultUtils.success(null);
	}

	/**
	 * 根据Id删除
	 */
	@PostMapping("/deleteChartById")
	public BaseResponse deleteChartById(Long id) {
		chartService.deleteChartById(id);
		return ResultUtils.success(null);
	}

	/**
	 * 智能分析（异步）
	 *
	 * @param multipartFile
	 * @param generateChartByAIRequest
	 * @param request
	 * @return
	 */
	@PostMapping("/generate/async")
	@ApiOperation(value = "图表智能分析", notes = "图表智能分析")
	public BaseResponse<BiResponseVO> generateChartByAIAsync(@RequestPart("file") MultipartFile multipartFile,
												  GenerateChartByAIRequest generateChartByAIRequest, HttpServletRequest request) {
		String name = generateChartByAIRequest.getName();
		String goal = generateChartByAIRequest.getGoal();
		String chartType = generateChartByAIRequest.getChartType();
		String chartId = generateChartByAIRequest.getId();
		this.validFile(multipartFile, FileUploadBizEnum.CHART_SHEET);
		if (StringTools.isEmpty(name) || StringTools.isEmpty(goal)) {
			throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数错误");
		}
		if (name.length() > 100) {
			throw new BusinessException(ErrorCode.PARAMS_ERROR, "名称不能超过 100 个字符");
		}

		String loginId = (String) StpUtil.getLoginId();
		// 限流判断
		redisLimitManage.doRateLimit(Const.LIMIT_CHART + loginId);

		// 根据图表文件，展示csv
		String csv = ExcelUtils.excelToCsv(multipartFile);
		StringBuilder sb = new StringBuilder();
		sb.append(Const.GEN_GOAL).append("\n");
		if (!StringTools.isEmpty(chartType)) {
			goal += Const.CHART_PREFIX + chartType;
		}
		sb.append(goal).append("\n");
		sb.append(Const.GEN_DATA).append("\n").append(csv).append("\n");

		// 保存到数据库
		Date curDate = new Date();
		Chart chart = new Chart();
		if (chartId == null) {
			chart.setId(IdUtil.getSnowflakeNextId());
		} else {
			chart.setId(Long.valueOf(chartId));
		}
		chart.setGoal(goal);
		chart.setName(name);
		chart.setChartData(csv);
		chart.setChartType(chartType);
		chart.setStatus(ChartStatusEnum.WAITING.getCode());
		chart.setUserId(Long.valueOf(loginId));

		chart.setUpdateTime(curDate);

		Integer saveChart = 0;
		if (chartId == null) {
			chart.setCreateTime(curDate);
			saveChart = chartService.saveChart(chart);
		} else {
			saveChart = chartService.updateChartById(chart, Long.valueOf(chartId));
		}

		if (saveChart == 0) {
			throw new BusinessException(ErrorCode.SYSTEM_ERROR, "保存图表{初始化}失败");
		}

		// 线程池执行任务
		CompletableFuture.runAsync(() -> {
			// 先修改图表任务状态为“执行中”，执行成功后，修改为“已完成”，执行失败后，修改为“失败”，记录失败信息
			Chart updateChart = new Chart();
			updateChart.setStatus(ChartStatusEnum.PROCESSING.getCode());
			log.info("{}",chart.getId());
			ChartQuery updateChartQuery = new ChartQuery();
			updateChartQuery.setStatus(ChartStatusEnum.WAITING.getCode());
			updateChartQuery.setId(chart.getId());
			Integer updateByParamCount = chartService.updateByParam(updateChart, updateChartQuery);
			if (updateByParamCount == 0) {
				handleError(updateChartQuery.getId(), "更新图表状态{等待}失败");
				return;
			}
			String result = sparkAiUtils.sendMessage(sb.toString());
			// 获取option
//		int option = result.indexOf("option");
//		String substring = result.substring(option, result.length());
			String[] split = result.split("=====");
			if (split.length != 3) {
				String answer = "根据这个回答模板 { " + Const.ANSWER_MODEL +" }, 帮我分析一下这个数据 ," + sb.toString();
				String resultMessage = sparkAiUtils.sendMessage(answer);
				split = resultMessage.split("=====");
			}
			if (split.length != 3) {
				handleError(updateChartQuery.getId(), "AI智能分析失败，格式有误");
				return;
			}
			if (StringTools.isEmpty(split[1].trim()) || StringTools.isEmpty(split[2].trim())) {
				handleError(updateChartQuery.getId(), "AI智能分析失败，数据为空");
				return;
			}
			updateChart.setGenChart(split[1].trim());
			updateChart.setGenResult(split[2].trim());
			updateChart.setStatus(ChartStatusEnum.FINISHED.getCode());
			updateChartQuery.setStatus(ChartStatusEnum.PROCESSING.getCode());
			updateByParamCount = chartService.updateByParam(updateChart, updateChartQuery);
			if (updateByParamCount == 0) {
				handleError(updateChartQuery.getId(), "更新图表状态{成功}失败");
			}
		}, threadPoolExecutor);

		BiResponseVO biResponseVO = BiResponseVO.builder()
				.chartId(chart.getId())
				.build();

		return ResultUtils.success(biResponseVO);
	}

	/**
	 * 智能分析（异步消息队列）
	 *
	 * @param multipartFile
	 * @param generateChartByAIRequest
	 * @param request
	 * @return
	 */
	@PostMapping("/generate/async/mq")
	@ApiOperation(value = "图表智能分析", notes = "图表智能分析")
	public BaseResponse<BiResponseVO> generateChartByAIAsyncMq(@RequestPart("file") MultipartFile multipartFile,
															 GenerateChartByAIRequest generateChartByAIRequest, HttpServletRequest request) {
		String name = generateChartByAIRequest.getName();
		String goal = generateChartByAIRequest.getGoal();
		String chartType = generateChartByAIRequest.getChartType();
		String chartId = generateChartByAIRequest.getId();
		this.validFile(multipartFile, FileUploadBizEnum.CHART_SHEET);
		if (StringTools.isEmpty(name) || StringTools.isEmpty(goal)) {
			throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数错误");
		}
		if (name.length() > 100) {
			throw new BusinessException(ErrorCode.PARAMS_ERROR, "名称不能超过 100 个字符");
		}

		String loginId = (String) StpUtil.getLoginId();
		// 限流判断
		redisLimitManage.doRateLimit(Const.LIMIT_CHART + loginId);

		// 根据图表文件，展示csv
		String csv = ExcelUtils.excelToCsv(multipartFile);


		// 保存到数据库
		Date curDate = new Date();
		Chart chart = new Chart();
		if (chartId == null) {
			chart.setId(IdUtil.getSnowflakeNextId());
		} else {
			chart.setId(Long.valueOf(chartId));
		}
		chart.setGoal(goal);
		chart.setName(name);
		chart.setChartData(csv);
		chart.setChartType(chartType);
		chart.setStatus(ChartStatusEnum.WAITING.getCode());
		chart.setUserId(Long.valueOf(loginId));

		chart.setUpdateTime(curDate);

		Integer saveChart = 0;
		if (chartId == null) {
			chart.setCreateTime(curDate);
			saveChart = chartService.saveChart(chart);
		} else {
			saveChart = chartService.updateChartById(chart, Long.valueOf(chartId));
		}

		if (saveChart == 0) {
			throw new BusinessException(ErrorCode.SYSTEM_ERROR, "保存图表{初始化}失败");
		}

		biMessageProducer.sendMessage(String.valueOf(chart.getId()));

		BiResponseVO biResponseVO = BiResponseVO.builder()
				.chartId(chart.getId())
				.build();

		return ResultUtils.success(biResponseVO);
	}


	private void handleError(Long chartId, String errMessage) {
		Chart chart = new Chart();
		chart.setStatus(ChartStatusEnum.ERROR.getCode());
		chart.setMessage(errMessage);
		ChartQuery chartQuery = new ChartQuery();
		chartQuery.setStatus(ChartStatusEnum.PROCESSING.getCode());
		chartQuery.setId(chartId);
		Integer updateByParamCount = chartService.updateByParam(chart, chartQuery);
		if (updateByParamCount == 0) {
			log.error("更新{失败}状态失败，chartId={}, errMessage={}", chartId, errMessage);
		}
	}

	/**
	 * 智能分析（同步）
	 *
	 * @param multipartFile
	 * @param generateChartByAIRequest
	 * @param request
	 * @return
	 */
	@PostMapping("/generate")
	@ApiOperation(value = "图表智能分析", notes = "图表智能分析")
	public BaseResponse<BiResponseVO> generateChartByAI(@RequestPart("file") MultipartFile multipartFile,
														GenerateChartByAIRequest generateChartByAIRequest, HttpServletRequest request) {
		String name = generateChartByAIRequest.getName();
		String goal = generateChartByAIRequest.getGoal();
		String chartType = generateChartByAIRequest.getChartType();
		this.validFile(multipartFile, FileUploadBizEnum.CHART_SHEET);
		if (StringTools.isEmpty(name) || StringTools.isEmpty(goal)) {
			throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数错误");
		}
		if (name.length() > 100) {
			throw new BusinessException(ErrorCode.PARAMS_ERROR, "名称不能超过 100 个字符");
		}

		String loginId = (String) StpUtil.getLoginId();
		// 限流判断
		redisLimitManage.doRateLimit(Const.LIMIT_CHART + loginId);

		// 根据图表文件，展示csv
		String csv = ExcelUtils.excelToCsv(multipartFile);
		StringBuilder sb = new StringBuilder();
		sb.append(Const.GEN_GOAL).append("\n");
		if (!StringTools.isEmpty(chartType)) {
			goal += Const.CHART_PREFIX + chartType;
		}
		sb.append(goal).append("\n");
		sb.append(Const.GEN_DATA).append("\n").append(csv).append("\n");
		String result = sparkAiUtils.sendMessage(sb.toString());
		// 获取option
//		int option = result.indexOf("option");
//		String substring = result.substring(option, result.length());
		String[] split = result.split("=====");
		if (split.length != 3) {
			String answer = "根据这个回答模板 { " + Const.ANSWER_MODEL +" }, 帮我分析一下这个数据 ," + sb.toString();
			String resultMessage = sparkAiUtils.sendMessage(answer);
			split = resultMessage.split("=====");
		}
		if (split.length != 3) {
			throw new BusinessException(ErrorCode.SYSTEM_ERROR, "AI智能分析失败");
		}
		if (StringTools.isEmpty(split[1].trim()) || StringTools.isEmpty(split[2].trim())) {
			throw new BusinessException(ErrorCode.SYSTEM_ERROR, "AI智能分析失败");
		}
		// 保存到数据库
		Date curDate = new Date();
		Chart chart = new Chart();
		chart.setGoal(goal);
		chart.setName(name);
		chart.setChartData(csv);
		chart.setChartType(chartType);
		chart.setGenChart(split[1].trim());
		chart.setGenResult(split[2].trim());
		chart.setUserId(Long.valueOf(loginId));
		chart.setCreateTime(curDate);
		chart.setUpdateTime(curDate);

		Integer saveChart = chartService.saveChart(chart);
		if (saveChart == 0) {
			throw new BusinessException(ErrorCode.SYSTEM_ERROR, "保存图表失败");
		}
		BiResponseVO biResponseVO = BiResponseVO.builder()
				.jsCode(split[1].trim())
				.conclusion(split[2].trim())
				.chartId(chart.getId())
				.build();

		return ResultUtils.success(biResponseVO);
	}

	/**
	 * 校验文件
	 *
	 * @param multipartFile
	 * @param fileUploadBizEnum 业务类型
	 */
	private void validFile(MultipartFile multipartFile, FileUploadBizEnum fileUploadBizEnum) {
		// 文件大小
		long fileSize = multipartFile.getSize();
		// 文件后缀
		String fileSuffix = FileUtil.getSuffix(multipartFile.getOriginalFilename());
		final long ONE_M = 1024 * 1024L;
		if (FileUploadBizEnum.USER_AVATAR.equals(fileUploadBizEnum)) {
			if (fileSize > ONE_M) {
				throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件大小不能超过 1M");
			}
			if (!Arrays.asList("jpeg", "jpg", "svg", "png", "webp").contains(fileSuffix)) {
				throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件类型错误");
			}
		} else if (FileUploadBizEnum.CHART_SHEET.equals(fileUploadBizEnum)) {
			if (fileSize > ONE_M) {
				throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件大小不能超过 1M");
			}
			if (!Arrays.asList("xls", "xlsx").contains(fileSuffix)) {
				throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件类型错误");
			}
		}

	}

}